#pragma once
#include "Submodule/Globals.h"

class MagicItemGenerator;
#include <map>
#include <vector>

struct Affix;
struct AffixLevel;
struct AffixList;

struct Affix {
	MagicItemGenerator* mig;
	UInt8 soul;
	double chance;

	Affix(MagicItemGenerator* migPtr, UInt8 soulLv, double prob)
		: mig(migPtr)
		, soul(soulLv)
		, chance(prob)
	{
	}
	Affix(const Affix& copy )
		: mig(copy.mig)
		, soul(copy.soul)
		, chance(copy.chance)
	{
	}
	bool operator==(const Affix& right) const {
		if ( soul != right.soul )
			return false;

		if ( chance != right.chance )
			return false;
		
		if ( mig->CompareTo(*const_cast<MagicItemGenerator*>(right.mig)) )
			return false;

		return true;
	}
};

struct AffixLevel {
	std::vector<Affix*> affs;
	double sumChances;

	void add(const Affix& toAdd) {
		affs.push_back(new Affix(toAdd));
		sumChances += toAdd.chance;
	}
	AffixLevel(const Affix& first)
		: sumChances(0)
		, affs(std::vector<Affix*>())
	{
		add(first);
	}
	~AffixLevel() {
		for ( std::vector<Affix*>::iterator i = affs.begin(); i != affs.end(); ++i ) {
			if ( *i ) {
				delete *i;
			}
		}
	}
	const AffixLevel& operator+(const AffixLevel& right) {
		for (std::vector<Affix*>::const_iterator i = right.affs.begin(); i != right.affs.end(); ++i) {
			for (std::vector<Affix*>::iterator j = affs.begin(); j != affs.end(); ++j) {
				if ( *i == *j )
					continue;
			}
			add(**i);
		}
		return *this;
	}
	void operator+=(const AffixLevel& right) {
		*this = *this + right;
	}
	bool hasPositive() const {
		for (std::vector<Affix*>::const_iterator i = affs.begin(); i != affs.end(); ++i) {
			if ((*i)->soul != Souls::kSoulIndex_Negative)
				return true;
		}
		return false;
	}
};

struct AffixList {
	std::map<UInt16, AffixLevel*> lvs;
	double sumChances;

	void add(const Affix& toAdd, UInt16 lv) {
		std::map<UInt16, AffixLevel*>::iterator lvit = lvs.find(lv);
		if ( lvit == lvs.end() )
			lvs.insert(std::pair<UInt16, AffixLevel*>(lv, new AffixLevel(toAdd)));
		else
			lvs[lv]->add(toAdd);
		sumChances += toAdd.chance;
	}
	AffixList()
		: lvs(std::map<UInt16, AffixLevel*>())
		, sumChances(0)
	{
	}
	~AffixList() {
		for ( std::map<UInt16, AffixLevel*>::iterator i = lvs.begin(); i != lvs.end(); ++i ) {
			if ( i->second ) {
				delete i->second;
			}
		}
	}
	const AffixList& operator+(const AffixList& right) {
		for (std::map<UInt16, AffixLevel*>::iterator i = lvs.begin(); i != lvs.end(); ++i)
			if ( right.lvs.find(i->first) != right.lvs.end() )
				*i->second += *right.lvs.find(i->first)->second;
		return *this;
	}
	void operator+=(const AffixList& right) {
		*this = *this + right;
	}
	Affix* chooseRandom() const {
		boost::uniform_real<double> chances = boost::uniform_real<double>(0, sumChances);
		boost::variate_generator<boost::mt19937&, boost::uniform_real<double> >gen(rng, chances);
		double prefixID = gen();
		for ( auto i = lvs.begin(); i != lvs.end(); ++i )
			if ( prefixID > i->second->sumChances )
				prefixID -= i->second->sumChances;
			else
				for ( std::vector<Affix*>::iterator j = i->second->affs.begin(); j != i->second->affs.end(); ++j )
					if ( prefixID > (*j)->chance )
						prefixID -= (*j)->chance;
					else
						return *j;
		return NULL;
	}
	bool hasPositive() const {
		for (auto i = lvs.begin(); i != lvs.end(); ++i) {
			if (i->second->hasPositive())
				return true;
		}
		return false;
	}
};
